package org.budo.redis.jdbc.driver.statement;

import java.io.InputStream;
import java.io.Reader;
import java.sql.PreparedStatement;
import java.sql.Timestamp;
import java.util.Map;

import org.budo.jdbc.driver.exception.BudoJdbcDriverNotSupportedException;
import org.budo.jdbc.driver.statement.AbstractJdbcStatement;
import org.budo.redis.jdbc.driver.BudoRedisJdbcPreparedStatement;
import org.budo.redis.jdbc.driver.parser.RedisSqlStatement;
import org.budo.support.lang.util.ArrayUtil;
import org.budo.support.lang.util.IoUtil;
import org.budo.support.lang.util.NumberUtil;

/**
 * @author lmw
 */
public abstract class AbstractRedisStatement extends AbstractJdbcStatement {
    protected static final String OK = "OK";

    /**
     * 获取第 index 个参数，替换占位符
     * 
     * @return 不会为空
     */
    protected byte[] parameter(Integer index, PreparedStatement preparedStatement) {
        Object parameter = this.functionParameterNoNull(index, preparedStatement);

        // 合法的参数输入
        if (parameter instanceof byte[] //
                || parameter instanceof String //
                || parameter instanceof InputStream //
                || parameter instanceof Reader) {
            return IoUtil.toByteArray(parameter);
        }

        throw new BudoJdbcDriverNotSupportedException("#37 parameter type error" //
                + ", this=" + this //
                + ", index=" + index //
                + ", preparedStatement=" + preparedStatement //
                + ", parameter=" + parameter //
                + ", parameter.type=" + parameter.getClass());
    }

    /**
     * 获取从 fromIndex 开始的所有参数，替换占位符
     * 
     * @return 数组中的每一个值都不会为空
     */
    protected byte[][] parameterArray(Integer fromIndex, PreparedStatement preparedStatement) {
        String[] functionParameters = ((RedisSqlStatement) this.getSqlStatement()).getFunctionParameters();
        String[] parameterArray = ArrayUtil.subArray(functionParameters, fromIndex);

        byte[][] valueArray = new byte[parameterArray.length][];
        for (int i = 0; i < parameterArray.length; i++) {
            valueArray[i] = this.parameter(fromIndex + i, preparedStatement);
        }

        return valueArray;
    }

    /**
     * 获取第 index 个参数，替换占位符，转换为Number
     * 
     * @return 不会为空
     */
    protected Number parameterNumber(Integer index, PreparedStatement preparedStatement) {
        Object value = this.functionParameterNoNull(index, preparedStatement);

        if (value instanceof Number) {
            return ((Number) value);
        }

        if (value instanceof String) {
            return NumberUtil.toDouble(value);
        }

        throw new BudoJdbcDriverNotSupportedException("#61 parameterNumber, type error, value=" + value + ", type=" + value.getClass());
    }

    /**
     * 获取第 index 个参数，替换占位符，转换为UnixTime
     * 
     * @return 不会为空
     */
    protected Long parameterUnixTime(Integer index, PreparedStatement preparedStatement) {
        Object value = this.functionParameterNoNull(index, preparedStatement);

        if (value instanceof Timestamp) {
            return ((Timestamp) value).getTime();
        }

        if (value instanceof Number) {
            return ((Number) value).longValue();
        }

        throw new BudoJdbcDriverNotSupportedException("#74 parameterUnixTime, type error, value=" + value + ", type=" + value.getClass());
    }

    private Object functionParameterNoNull(Integer index, PreparedStatement preparedStatement) {
        String[] functionParameters = ((RedisSqlStatement) this.getSqlStatement()).getFunctionParameters();

        if (functionParameters.length <= index) {
            throw new BudoJdbcDriverNotSupportedException("#78 parameter not found, sqlStatement=" + this.getSqlStatement() //
                    + ", index=" + index + ", preparedStatement=" + preparedStatement);
        }

        String parameter = functionParameters[index];
        if (null == parameter) {
            throw new BudoJdbcDriverNotSupportedException("#84 parameter is null, parameter=" + parameter + ", index=" + index //
                    + ", preparedStatement=" + preparedStatement);
        }

        if ("?".equals(parameter)) {
            Integer parameterIndex = ((BudoRedisJdbcPreparedStatement) preparedStatement).incrementAndGetParameterIndex();
            Map<Integer, Object> parameterMap = ((BudoRedisJdbcPreparedStatement) preparedStatement).getParameterMap();
            Object parameterValue = parameterMap.get(parameterIndex);
            if (null == parameterValue) {
                throw new BudoJdbcDriverNotSupportedException("#97 parameter value is null, parameter=" + parameter + ", index=" + index //
                        + ", value=" + parameterValue + ", preparedStatement=" + preparedStatement);
            }

            return parameterValue;
        }

        // 如果使用这个特性，就应该全局使用，因为这会导致前面的incrementAndGet不正确
        // 指定index的参数 ?1
        if (parameter.length() > 1 //
                && '?' == parameter.charAt(0) //
                && Character.isDigit(parameter.charAt(1))) {
            Integer parameterIndex = NumberUtil.toInteger(parameter.substring(1));

            Map<Integer, Object> parameterMap = ((BudoRedisJdbcPreparedStatement) preparedStatement).getParameterMap();
            Object parameterValue = parameterMap.get(parameterIndex);
            if (null == parameterValue) {
                throw new BudoJdbcDriverNotSupportedException("#114 parameter value is null, parameter=" + parameter + ", index=" + index //
                        + ", value=" + parameterValue + ", preparedStatement=" + preparedStatement);
            }

            return parameterValue;
        }

        return parameter;
    }

    @Override
    public String toString() {
        return super.toString() + ", sqlStatement=" + this.getSqlStatement();
    }
}